package com.zhx.common.common.event.Listener;


import com.zhx.common.chat.dao.GroupMemberDao;
import com.zhx.common.chat.domain.entity.GroupMember;
import com.zhx.common.chat.domain.entity.RoomGroup;
import com.zhx.common.chat.domain.vo.request.ChatMessageReq;
import com.zhx.common.chat.service.ChatService;
import com.zhx.common.chat.service.adapter.MemberAdapter;
import com.zhx.common.chat.service.adapter.RoomAdapter;
import com.zhx.common.chat.service.cache.GroupMemberCache;
import com.zhx.common.chat.service.cache.MsgCache;
import com.zhx.common.common.event.GroupMemberAddEvent;
import com.zhx.common.user.dao.UserDao;
import com.zhx.common.user.domain.entity.User;
import com.zhx.common.user.service.cache.UserInfoCache;
import com.zhx.common.user.service.impl.PushService;
import com.zhx.common.websocket.domain.vo.resp.WSBaseResp;
import com.zhx.common.websocket.domain.vo.resp.WSMemberChange;
import com.zhx.common.websocket.service.WebSocketService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionalEventListener;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 添加群成员监听器
 *
 * @author zhongzb create on 2022/08/26
 */
@Slf4j
@Component
public class GroupMemberAddListener {
    @Autowired
    private WebSocketService webSocketService;
    @Autowired
    private ChatService chatService;
    @Autowired
    private MsgCache msgCache;
    @Autowired
    private UserInfoCache userInfoCache;
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;
    @Autowired
    private GroupMemberDao groupMemberDao;
    @Autowired
    private UserDao userDao;
    @Autowired
    private GroupMemberCache groupMemberCache;
    @Autowired
    private PushService pushService;


    /**
     * 异步发送添加群成员的消息。
     * 使用事件驱动模型，当有群成员添加事件（GroupMemberAddEvent）发生时，触发此方法。
     * 方法将为新增的群成员发送一条加入群组的消息。
     *
     * @param event 包含群组成员添加信息的事件对象。包含以下重要数据：
     *              - groupMembers 被添加的群成员列表
     *              - roomGroup 相关的群组信息
     *              - uid 邀请人的用户ID
     */
    @Async
    @TransactionalEventListener(classes = GroupMemberAddEvent.class, fallbackExecution = true)
    public void sendAddMsg(GroupMemberAddEvent event) {
        // 从事件中获取群成员列表、群组信息和邀请人的UID
        List<GroupMember> memberList = event.getGroupMembers();
        RoomGroup roomGroup = event.getRoomGroup();
        Long inviteUid = event.getUid();

        // 根据邀请人的UID获取用户信息
        User user = userInfoCache.get(inviteUid);
        // 提取群成员UID列表，以便后续批量获取用户信息
        List<Long> uidList = memberList.stream().map(GroupMember::getUid).collect(Collectors.toList());

        // 根据群组信息、邀请人信息和目标群成员信息（批量获取），构建群添加消息
        ChatMessageReq chatMessageReq = RoomAdapter.buildGroupAddMessage(roomGroup, user, userInfoCache.getBatch(uidList));
        // 发送消息，消息来源设定为系统
        chatService.sendMsg(chatMessageReq, User.UID_SYSTEM);
    }


    /**
     * 异步发送群组成员变动推送消息。
     * 该方法会在交易完成后执行，如果执行失败会进行回退。
     *
     * @param event 包含群组成员添加信息的事件对象。其中包含了发生变动的群组成员列表和相关的群组信息。
     */
    @Async
    @TransactionalEventListener(classes = GroupMemberAddEvent.class, fallbackExecution = true)
    public void sendChangePush(GroupMemberAddEvent event) {
        // 从事件中获取群组成员列表和相关的群组对象
        List<GroupMember> memberList = event.getGroupMembers();
        RoomGroup roomGroup = event.getRoomGroup();

        // 从缓存中获取当前群组的所有成员UID列表
        List<Long> memberUidList = groupMemberCache.getMemberUidList(roomGroup.getRoomId());

        // 从群组成员列表中提取UID并收集到一个新的列表中
        List<Long> uidList = memberList.stream().map(GroupMember::getUid).collect(Collectors.toList());

        // 根据收集到的UID列表查询对应的用户信息
        List<User> users = userDao.listByIds(uidList);

        // 遍历查询到的用户信息，为每个用户构建并发送相应的推送消息
        users.forEach(user -> {
            WSBaseResp<WSMemberChange> ws = MemberAdapter.buildMemberAddWS(roomGroup.getRoomId(), user);
            pushService.sendPushMsg(ws, memberUidList);
        });

        // 在所有推送消息发送完成后，移除该群组的成员UID缓存
        groupMemberCache.evictMemberUidList(roomGroup.getRoomId());
    }

}
